#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <pthread.h>
#include <sys/wait.h>
#include <ctype.h>

#define IsSpace(x) isspace((int)(x))

#define SERVER_SETTING "Server: echttpd/1.0\r\n"
#define SERVER_NAME "echttpd/1.0"

int start_up(u_short *);
int get_first_line(int ,char *,int);
int unimplemented(int);
int not_found(int);
int serve_static_file(int, char *, int);
int http_headers(int, char *);
int get_file_size(char *filename);
int http_cat(int, FILE *);
int error_request(int);
void cgi_process(int ,const char *,char *,  const char *);


/********************************************************************/

/*
*  main - main routine for the server
初始化 httpd 服务，包括建立套接字，绑定端口，进行监听。
*/
int start_up(u_short *port){
    int httpd = 0;
    struct sockaddr_in name;

    //create socket
    httpd = socket(PF_INET, SOCK_STREAM, 0);
    if(httpd == -1)
    {
        perror("socket create failed");
        exit(1);
    }
    memset(&name, 0, sizeof(name));
    name.sin_family = AF_INET;
    name.sin_port = htons(*port);
    name.sin_addr.s_addr = htonl(INADDR_ANY);

    //设置端口可以重用
    int optval = 1;
    setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

    //bind port
    if(bind(httpd, (struct sockaddr *)&name, sizeof(name)) == -1)
    {
        perror("bind failed");
        exit(1);
    }
    //listen port
    // if dynamically allocating a port 
    if (*port == 0)
    {
        int namelen = sizeof(name);
        if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1){
            perror("getsockname failed");
            exit(1);
        }
    }

    if (listen(httpd, 5) == -1)
    {
        perror("listen failed");
        exit(1);
    }
    
    return httpd;
}

/*
* accept_request - accept a request and process it
*/
int accept_request(int client){
    char buf[1024];
    int numchars;
    char method[255];
    char url[255];
    char path[512];
    size_t i, j;
    struct stat st;
    int cgi = 0;
    char *query_string = NULL;
    //获得第一行内容
    numchars = get_first_line(client, buf, sizeof(buf));
    i = 0; j = 0;
    //把客户端的请求方法存放到method数组
    while (!IsSpace(buf[j]) && (i < sizeof(method) - 1))
    {
        method[i] = buf[j];
        i++; j++;
    }
    method[i] = '\0';

    //判断处理请求类型
    if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
    {
        //method不是GET或POST，返回错误
        return unimplemented(client);
    }

    //post 请求
    if (strcasecmp(method, "POST") == 0)
    {
        cgi = 0;
    }

    //把客户端的POST请求url存放到url数组
    i = 0;
    while (IsSpace(buf[j]) && (j < sizeof(buf)))
    {
        j++;
    }
    while (!IsSpace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)))
    {
        url[i] = buf[j];
        i++; j++;
    }
    url[i] = '\0';

    //Get 请求
    if (strcasecmp(method, "GET") == 0)
    {
        query_string = url;
        while (*query_string != '?' && *query_string != '\0')
        {
            query_string++;
        }
        if (*query_string == '?')
        {
            cgi = 1;
            *query_string = '\0';
            query_string++;
        }
    }

    //把客户端的url存放到path数组，html存放在htdocs中 
    sprintf(path, "htdocs%s", url);
    if (path[strlen(path) - 1] == '/')
    {
        strcat(path, "Bing.html");
    }
    if (stat(path, &st) == -1)
    {
        //文件不存在，返回错误
        while ((numchars > 0) && strcmp("\n", buf))
        {
            numchars = get_first_line(client, buf, sizeof(buf));
        }
        //返回404错误
        return not_found(client);
    }else{
        //文件存在，返回文件内容
        if((st.st_mode & S_IFMT) == S_IFDIR){
            strcat(path, "/Bing.html");
        }
        if((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)){
            cgi = 1;
        }
        if (!cgi)
        {
            serve_static_file(client, path, st.st_size);
        }else{
            cgi_process(client,path,method,query_string);
        }

        
    }
    //关闭套接字
    close(client);
    return 0;
}


// cgi处理函数
void cgi_process(int client,const char *path,char *method,  const char *query_string){
    char buf[1024];
    int cgi_output[2];
    int cgi_input[2];
    pid_t pid;
    int status;
    int i;
    char c;
    int numchars = 1;
    int content_length = -1;

    buf[0] = 'A';buf[1] = '\0';
    if (strcasecmp("POST", method) == 0)
    {
        numchars = get_first_line(client, buf, sizeof(buf));
        while ((numchars > 0) && strcmp("\n", buf))
        {
             /*利用 \0 进行分隔 */
            buf[15] = '\0';
            /* HTTP 请求的特点*/
            if (strcasecmp(buf, "Content-Length:") == 0)
            {
                content_length = atoi(&(buf[16]));
            }
            numchars = get_first_line(client, buf, sizeof(buf));
        }
        printf("content_length:%d\n",content_length);
        if(content_length == -1){
            error_request(client);
            return ;
        }
    }else{
        while ((numchars > 0) && strcmp("\n", buf))
        {
            numchars = get_first_line(client, buf, sizeof(buf));
        }
    }
    sprintf(buf, "HTTP/1.0 200 OK\r\n");
    send(client, buf, strlen(buf), 0);

    //创建管道
    if (pipe(cgi_output) < 0)
    {
        error_request(client);
        return;
    }
    if (pipe(cgi_input) < 0)
    {
        error_request(client);
        return;
    }
    //创建子进程
    if ((pid = fork()) < 0)
    {
         error_request(client);
        return ;
    }
    //子进程
    if (pid == 0)
    {
        //关闭读端
        char meth_env[255];
        char query_env[255];
        char length_env[255];
        //将环境变量传递给子进程
        dup2(cgi_output[1], 1);
        //将环境变量传递给子进程
        dup2(cgi_input[0], 0);
        //关闭读端
        close(cgi_output[0]);
        //关闭写端
        close(cgi_input[1]);
        //将环境变量传递给子进程
        sprintf(meth_env, "REQUEST_METHOD=%s", method);
        putenv(meth_env);
        if (strcasecmp("POST", method) == 0)
        {
            sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
            putenv(length_env);
        }else{
            sprintf(query_env, "QUERY_STRING=%s", query_string);
            putenv(query_env);
        }
        printf("execl error\n");
        exit(0);
    }else{
        //关闭写端
        close(cgi_output[1]);
        //关闭读端
        close(cgi_input[0]);
        //将环境变量传递给子进程
        if (strcasecmp("POST", method) == 0)
        {
            for (i = 0; i < content_length; i++)
            {
                recv(client, &c, 1, 0);
                //将数据写入管道
                write(cgi_input[1], &c, 1);
            }
            while (read(cgi_output[0], &c, 1) > 0)
            {
                //将数据写入管道
                send(client, &c, 1, 0);
            }
            //关闭管道
            close(cgi_output[0]);
            close(cgi_input[1]);
            //等待子进程
            waitpid(pid, &status, 0);
        }

    }
   
}


/*
get the first line of the request
*/
int get_first_line(int socket,char *buffer,int size){
    int i = 0;
    char end_char = '\0';
    int number = 0;

    while (i < (size-1) && end_char != '\n')
    {
        number = recv(socket, &end_char, 1, 0);
        if (number <= 0){
            end_char = '\n';
        }else{
            if (end_char == '\r')
            {
                number = recv(socket,&end_char,1,MSG_PEEK);
                printf("%02X\n",end_char);
                //这里是去掉其他换行符
                if (number > 0 && end_char == '\n')
                {
                    recv(socket,&end_char,1,0);
                }
                else
                {
                    end_char = '\n';
                }
            }
            buffer[i] = end_char;
            i++;
        }
    }
    buffer[i] = '\0';
    return i;
}

//处理未实现的请求
int unimplemented(int client){
    char buf[1024];
    sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, SERVER_SETTING);
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-Type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</TITLE></HEAD>\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</P></BODY></HTML>\r\n");
    send(client, buf, strlen(buf), 0);
    return -1;
}

//处理未找到的请求
int not_found(int client){
    char buf[1024];
    sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, SERVER_SETTING);
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-Type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<HTML><HEAD><TITLE>Not Found\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</TITLE></HEAD>\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "your request because the resource specified\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "is unavailable or nonexistent.\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</P></BODY></HTML>\r\n");
    send(client, buf, strlen(buf), 0);
    return -1;
}

//处理错误的请求
int error_request(int client){
    char buf[1024];
    sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, SERVER_SETTING);
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-Type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<HTML><HEAD><TITLE>Bad Request\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</TITLE></HEAD>\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "your request because the syntax of the\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "request was incorrect.\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</P></BODY></HTML>\r\n");
    send(client, buf, strlen(buf), 0);
    return -1;
}

//发送regular file给客户端
int serve_static_file(int client, char *filename, int filesize){
    FILE *resource = NULL;
    int numchars = 1;
    char buf[1024];

    //读取文件内容
    buf[0] = 'A';buf[1] = '\0';
    while ((numchars > 0) && strcmp("\n", buf))
    {
        numchars = get_first_line(client, buf, sizeof(buf));
    }
    //打开server的文件
    resource = fopen(filename, "r");
    if (resource == NULL)
    {
        return not_found(client);
    }else{
        //发送文件内容
        int rec = http_headers(client, filename);
        if(rec == -1){
            return -1;
        }else{
            int v = http_cat(client, resource);
        }

    } 
    fclose(resource);
    return 0; 
}

int get_file_size(char *filename){
    struct stat st;
    stat(filename, &st);
    return st.st_size;
}

//header头部
int http_headers(int client, char *filename){
    char buf[1024];
    sprintf(buf, "HTTP/1.0 200 OK\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, SERVER_SETTING);
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-Type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-Length: %d\r\n", get_file_size(filename));
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
    return 0;
}

//put entire constents of a file out on a socket
int http_cat(int client, FILE *resource){
    char buf[1024];
    int numchars;
    while ((numchars = fread(buf, 1, sizeof(buf), resource)) > 0)
    {
        if (send(client, buf, numchars, 0) != numchars)
        {
            fprintf(stderr, "Error sending data\n");
            exit(1);
        }
    }
    return 0;
}

int main(void)
{
    int server_sockfd, client_sockfd;
    u_short port = 1234;
    struct sockaddr_in server_addr;
    int client_len = sizeof(server_addr);
    pthread_t tid;

    //创建socket
    server_sockfd = start_up(&port);
    printf("Server is running on port %d\n", port);

    while (1)
    {
        //接受客户端的请求
        client_sockfd = accept(server_sockfd, (struct sockaddr *)&server_addr, &client_len);
        if (client_sockfd < 0)
        {
            fprintf(stderr, "Error on accept\n");
            exit(1);
        }
   
        //打印当前时间
        time_t t;
        time(&t);
        printf("Accepted connection from %s ,Time:%s\n", inet_ntoa(server_addr.sin_addr),ctime(&t));

        //创建线程
        if(pthread_create(&tid, NULL, accept_request, client_sockfd))
        {
            fprintf(stderr, "Error on pthread_create\n");
            exit(1);
        }
    }
    close(server_sockfd);
    return 0;
}